package com.library.service;

import com.library.config.JwtUtil;
import com.library.dto.LoginRequest;
import com.library.dto.LoginResponse;
import com.library.dto.RegisterRequest;
import com.library.dto.RegisterResponse;
import com.library.entity.User;
import com.library.exception.BusinessException;
import com.library.exception.UnauthorizedException;
import com.library.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 认证服务类
 * 负责处理用户登录、注册等认证相关业务逻辑
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthService {
    // 用户数据访问层，用于数据库操作
    private final UserRepository userRepository;
    // 密码加密器，使用BCrypt算法加密密码
    private final PasswordEncoder passwordEncoder;
    // JWT工具类，用于生成和验证JWT令牌
    private final JwtUtil jwtUtil;

    /**
     * 用户登录方法
     * 验证用户名和密码，验证成功后生成JWT令牌返回
     * 
     * @param request 登录请求，包含用户名和密码
     * @return LoginResponse 包含登录结果、JWT令牌和用户信息
     * @throws UnauthorizedException 当用户名不存在或密码错误时抛出
     */
    public LoginResponse login(LoginRequest request) {
        log.info("用户尝试登录: {}", request.getUsername());
        
        // 1. 根据用户名查找用户，如果不存在则抛出异常
        User user = userRepository.findByUsername(request.getUsername())
                .orElseThrow(() -> {
                    log.warn("登录失败: 用户名不存在 - {}", request.getUsername());
                    return new UnauthorizedException("用户名或密码错误");
                });

        // 2. 验证密码：使用BCrypt比较明文密码和加密后的密码
        // matches方法会自动处理BCrypt的salt和hash比较
        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            log.warn("登录失败: 密码错误 - {}", request.getUsername());
            throw new UnauthorizedException("用户名或密码错误");
        }

        // 3. 密码验证通过，生成JWT令牌
        // JWT包含用户ID、用户名和角色信息
        String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole());
        log.info("用户登录成功: {} (角色: {})", user.getUsername(), user.getRole());

        // 4. 构建用户信息对象
        LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo(
                user.getId(), user.getUsername(), user.getRole());

        // 5. 返回登录响应，包含令牌和用户信息
        return new LoginResponse("登录成功", token, userInfo);
    }

    /**
     * 用户注册方法
     * 创建新用户账户，密码会自动加密存储，注册成功后自动登录
     * 
     * @param request 注册请求，包含用户名和密码
     * @return RegisterResponse 包含注册结果、JWT令牌和用户信息
     * @throws BusinessException 当用户名已存在时抛出
     */
    @Transactional  // 使用事务保证数据一致性
    public RegisterResponse register(RegisterRequest request) {
        log.info("用户尝试注册: {}", request.getUsername());
        
        // 1. 检查用户名是否已存在
        if (userRepository.existsByUsername(request.getUsername())) {
            log.warn("注册失败: 用户名已存在 - {}", request.getUsername());
            throw new BusinessException("用户名已存在");
        }

        // 2. 创建新用户对象
        User newUser = new User();
        newUser.setUsername(request.getUsername());
        // 3. 使用BCrypt加密密码后存储
        // encode方法会自动生成salt并加密密码，每次加密结果都不同但都能正确验证
        newUser.setPassword(passwordEncoder.encode(request.getPassword()));
        // 4.设置用户邮箱和手机号
        newUser.setEmail(request.getEmail());
        newUser.setPhone(request.getPhone());
        // 4. 新注册用户默认为普通用户角色
        newUser.setRole("user");
        
        // 5. 保存用户到数据库
        newUser = userRepository.save(newUser);
        //System.out.println("保存后的用户对象：" + newUser);

        log.info("用户注册成功: {} (ID: {})", newUser.getUsername(), newUser.getId());

        // 6. 注册成功后自动登录，生成JWT令牌
        String token = jwtUtil.generateToken(newUser.getId(), newUser.getUsername(), newUser.getRole());
        
        // 7. 构建用户信息对象
        RegisterResponse.UserInfo userInfo = new RegisterResponse.UserInfo(
                newUser.getId(), newUser.getUsername(), newUser.getEmail(), newUser.getPhone(), newUser.getRole());

        // 8. 返回注册响应，包含令牌和用户信息
        return new RegisterResponse("注册成功", token, userInfo);
    }
}
